/*	Copyright 2007 - Xavier Baro (xbaro@cvc.uab.cat)

This file is part of eapmlib.

Eapmlib is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or any 
later version.

Eapmlib is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "EAPMSample.h"
#include "NaiveBayesModel.h"
#include "Chromosome.h"

#include <fstream>


using namespace std;
using namespace Evolutive;

int main(int argc,char* argv[]) 
{	
	// Redirect the clog stream to a file
	ofstream out("LogFile.txt");
	if ( out )
		clog.rdbuf(out.rdbuf());
	else
		cerr << "Error while opening the file" << endl;
	/* Define the evolutionary algorithms parameters */
	// Population size
	int PopSize=200;

	// Maximum number of iterations
	int MaxIters=200;

	// Number of runs for the experiment
	int NumRuns=10;

	// Number of variables to optimize (dimension of the problem)
	int NumVars=64;

	// Predefined functions:
	//    Maximization problems: PREDFUNC_SUM, PREDFUNC_4_PEAKS, PREDFUNC_HIFF, PREDFUNC_ISOPEAK, PREDFUNC_ISOTORUS
	//    Minimization problems: PREDEF_SPHERE,PREDEF_RASTRIGIN,PREDEF_ROSENBROCK,PREDEF_GRIEWANGK,PREDEF_ACKLEYS

	PREDEF_FUNCTION Problems[]={PREDFUNC_ONEMAX,PREDFUNC_PLATEAU,PREDFUNC_CHECKERBOARD,PREDFUNC_EQPRODUCTS,PREDFUNC_6_PEAKS,PREDFUNC_HIFF, PREDFUNC_ISOPEAK, PREDFUNC_ISOTORUS};
	
	for(int i=0;i<8;i++)
		PDMNBEOptimizeFunction(Problems[i],NumVars,PopSize,MaxIters,NumRuns,0);

	cout << "Press a key to continue..." << endl; 
	getchar();

	return 0;
}

void CheckConversions(void)
{
	unsigned long long Mask64=1;
	CChromosome C;
	
	for(int i=0; i<=64;i++)
	{
		cout << i << " " << (Mask64<<i) << " " << endl;
	}
	Mask64=0;
	cout << (1>>Mask64) << endl;

	double Number;
	char a[33];
	char b[65];
	unsigned char BitString[64];
		
	Number=118.234;
	{
		cout << "Initial Number:  " << Number << endl;
		C.Dbl2Bin(Number,BitString,64);
		cout << "Returned Number: " << C.Bin2Dbl(BitString,64) << endl;
	}

	printf("End of program, press Enter key to quit\n");
	getchar();
}

void GetProblemObjects(PREDEF_FUNCTION Function,int NumVars,CEAPMLearner *oL,CFuncEvaluator *FuncObject,double Parameters[2])
{
	// Set functions parameters
	int PeaksThr=15;
	int PlateauK=4;
	double EqProdK=4.0;
	double EqProdSeed=0;

	// Prepare the function evaluator
	FuncObject->SetFunction(Function,NumVars);	

	// Store the default parameters
	switch(Function)
	{
		case PREDFUNC_4_PEAKS:
		case PREDFUNC_6_PEAKS:
			Parameters[0]=PeaksThr;
		case PREDFUNC_PLATEAU:
			Parameters[0]=PlateauK;
		case PREDFUNC_EQPRODUCTS:
			Parameters[0]=EqProdK;
			Parameters[1]=EqProdSeed;
	}

	// Define the optimization problem
	switch(Function)
	{
	// Maximization binary problems
	case PREDFUNC_SUM:
	case PREDFUNC_ONEMAX:
	case PREDFUNC_HIFF:
	case PREDFUNC_ISOPEAK:
	case PREDFUNC_ISOTORUS:
	case PREDFUNC_CHECKERBOARD:
		oL->SetOptCriteria(Evolutive::OPT_MAXIMIZE);
		FuncObject->SetFncParameters(NULL);
		oL->SetCodingMethod(CODE_GRAY);
		break;		
	// Minimization numeric problems
	case PREDEF_SPHERE:
	case PREDEF_RASTRIGIN:
	case PREDEF_ROSENBROCK:
	case PREDEF_GRIEWANGK:
	case PREDEF_ACKLEYS:
		oL->SetOptCriteria(Evolutive::OPT_MINIMIZE);
		FuncObject->SetFncParameters(NULL);
		oL->SetCodingMethod(CODE_DECIMAL_DBL);
		break;
	// Maximization binary functions with parameters
	case PREDFUNC_4_PEAKS:
	case PREDFUNC_6_PEAKS:
	case PREDFUNC_PLATEAU:		
		FuncObject->SetFncParameters(Parameters);
		oL->SetOptCriteria(Evolutive::OPT_MAXIMIZE);
		oL->SetCodingMethod(CODE_GRAY);
		break;
	// Minimization binary functions with parameters
	case PREDFUNC_EQPRODUCTS:
		FuncObject->SetFncParameters(Parameters);
		oL->SetOptCriteria(Evolutive::OPT_MINIMIZE);
		oL->SetCodingMethod(CODE_GRAY);
		break;
	}
}

void OptimizeFunction(PREDEF_FUNCTION Function,int NumVars,int NumIndividuals,int MaxIters, int NumRuns,double WorsePercentage)
{
	CEAPMLearner oL;	
	CNaiveBayesModel oNBM;
	CChromosome SolInd;
	double SolScore;
	int NumIter;
	PREDEF_FUNCTION GoalFnc;
	CODING_METHOD CodingMethod;
	register int i;
	double BestValue,EvalNum;
	double ObjectVal=-1;
	CFuncEvaluator FuncObject;
	double Parameters[2];

	// Set functions parameters
	int Var4PeaksThr=10;

	// Prepare the function evaluator
	FuncObject.SetFunction(Function,NumVars);	

	// Define the optimization problem
	switch(Function)
	{
	// Maximization binary problems without parameters
	case PREDFUNC_SUM:
	case PREDFUNC_ONEMAX:
	case PREDFUNC_HIFF:
	case PREDFUNC_ISOPEAK:
	case PREDFUNC_ISOTORUS:
	case PREDFUNC_CHECKERBOARD:
		oL.SetOptCriteria(Evolutive::OPT_MAXIMIZE);
		FuncObject.SetFncParameters(NULL);
		CodingMethod=CODE_GRAY;		
		break;		
	// Minimization numeric problems without parameters
	case PREDEF_SPHERE:
	case PREDEF_RASTRIGIN:
	case PREDEF_ROSENBROCK:
	case PREDEF_GRIEWANGK:
	case PREDEF_ACKLEYS:
		oL.SetOptCriteria(Evolutive::OPT_MINIMIZE);
		FuncObject.SetFncParameters(NULL);
		CodingMethod=CODE_DECIMAL_DBL;		
		break;
	// Four/six-peaks function
	case PREDFUNC_4_PEAKS: 
	case PREDFUNC_6_PEAKS:
		Parameters[0]=Var4PeaksThr;
		FuncObject.SetFncParameters(Parameters);
		oL.SetOptCriteria(Evolutive::OPT_MAXIMIZE);
		CodingMethod=CODE_GRAY;		
		break;

	// Plateau function
	case PREDFUNC_PLATEAU:
		// K=m_Parameters[0]
		
		break;
	case PREDFUNC_EQPRODUCTS:
		// K=m_Parameters[0]
		// m_Parameters[1] contains the seed for random numbers generation or -1
		
		break;
	}
	
	// Configure the probability model
	oNBM.SetGenerationMethod(POPGEN_VAL);
	oNBM.SetEstimationWorseElements(WorsePercentage);

	// Configure the learner	
	oL.SetModelTolerance(0.005);
	oL.SetPopulationSize(NumIndividuals);	
	oL.SetEvaluator(&FuncObject);	
	oL.SetProbModel(&oNBM);	
	oL.SetEvolStrategy(EVOL_ESTIMATE);	
	oL.SetNumElitist(4);
	oL.SetNumEstimation(cvRound(NumIndividuals*0.75));
	//oL.SetSelCriteria(SELMETHOD_FASTDIVERSITY)
	oL.SetSelCriteria(SELMETHOD_SCORE);
	oL.SetCodingMethod(CodingMethod);

	// Set the stop criteria
	ObjectVal=FuncObject.GetOptimumValue();
	oL.SetMaxIterations(MaxIters);
	if(ObjectVal>-1)
		oL.SetStopValue(ObjectVal);

	// Initialize the output
	BestValue=0.0;
	EvalNum=0.0;

	// Solve the problem
	for(i=0;i<NumRuns;i++)
	{
		// Show the progress
		clog << "\rIteration: " << i+1 << " of " << NumRuns << endl; 

		// Solve the problem
		SolScore=oL.Solve(SolInd,NumIter);

		// Add to the result
		BestValue+=SolScore;
		EvalNum+=NumIter*NumIndividuals;	

		// Add an extra line to maintain the result of the evaluation
		clog <<endl;
	}

	// Divide by the number of runs to obtain the mean value
	BestValue/=NumRuns;
	EvalNum/=NumRuns;	

	// Show the results
	clog << "Final Value (Mean): " << BestValue << endl;
	clog << "Number Evaluations (Mean): " << EvalNum << endl;
}


void PDMNBEOptimizeFunction(PREDEF_FUNCTION Function,int NumVars,int NumIndividuals,int MaxIters, int NumRuns,double WorsePercentage)
{
	char Buffer[128];
	CEAPMLearner oLearner;	
	CChromosome SolInd;
	double SolScore;
	int NumIter;
	register int i;
	double BestValue,EvalNum;
	double ObjectVal=-1;
	CFuncEvaluator FuncObject;
	double Parameters[2];

	CNaiveBayesModel oNBM;

	// Configure the learner	
	oLearner.SetModelTolerance(0.0005);
	oLearner.SetPopulationSize(NumIndividuals);	
	oLearner.SetEvaluator(&FuncObject);	
	oLearner.SetEvolStrategy(EVOL_ESTIMATE);	
	oLearner.SetNumElitist(1);
	oLearner.SetNumEstimation(cvRound(NumIndividuals*0.75));
	oLearner.SetSelCriteria(SELMETHOD_SCORE);
	//oLearner.SetSelCriteria(SELMETHOD_FASTDIVERSITY);

	// Configure the probability model
	oNBM.SetGenerationMethod(POPGEN_VAL);
	//oNBM.SetGenerationMethod(POPGEN_PROB);
	oNBM.SetEstimationWorseElements(WorsePercentage);
	oLearner.SetProbModel(&oNBM);	


	// Set parameters for this problem
	GetProblemObjects(Function,NumVars,&oLearner,&FuncObject,Parameters);

	// Set the stop criteria
	ObjectVal=FuncObject.GetOptimumValue();
	oLearner.SetMaxIterations(MaxIters);
	if(ObjectVal>-1)
		oLearner.SetStopValue(ObjectVal);

	// Show problem details


	// Initialize the output
	BestValue=0.0;
	EvalNum=0.0;

	// Show problem details
	clog << endl << "Problem: " << FuncObject.GetProblemName(Buffer) << endl;
	clog << "Optimum Value: " << FuncObject.GetOptimumValue() << endl;

	// Solve the problem
	for(i=0;i<NumRuns;i++)
	{
		// Show the progress
		clog << "Iteration: " << i+1 << " of " << NumRuns << endl; 

		// Solve the problem
		SolScore=oLearner.Solve(SolInd,NumIter);

		// Add to the result
		BestValue+=SolScore;
		EvalNum+=NumIter*NumIndividuals;	

		// Add an extra line to maintain the result of the evaluation
		clog << "\t" << SolScore << endl;
	}

	// Divide by the number of runs to obtain the mean value
	BestValue/=NumRuns;
	EvalNum/=NumRuns;	

	// Show the results
	clog << "Final Value (Mean): " << BestValue << endl;
	clog << "Number Evaluations (Mean): " << EvalNum << endl;
}